/**
 * @file main.c
 * @author MakerInChina (makerinchina.cn)
 * @brief 
 * @version 0.01
 * @date 2022-09-27
 * 
 * @copyright Copyright (c) 2022
 * 
 */


#include <libopencm3/stm32/rcc.h>
#include <libopencm3/stm32/gpio.h>
#include <libopencm3/cm3/systick.h>
#include <libopencm3/stm32/usart.h>
#include <libopencm3/cm3/nvic.h>

#include <stdio.h>
#include <string.h>

#include "FreeRTOS.h"
#include "task.h"
#include "queue.h"



#include "FreeRTOS_CLI.h"

xQueueHandle uart_queue;

volatile uint8_t recv_index = 0;
volatile uint8_t send_index = 0;

#define BUFF_SIZE 64
uint8_t recv_buff[BUFF_SIZE] = {0};

/*
 * Called by libc stdio fwrite functions
 */
int
_write(int fd, char *ptr, int len)
{
	int i = 0;

	/*
	 * Write "len" of char from "ptr" to file id "fd"
	 * Return number of char written.
	 *
	 * Only work for STDOUT, STDIN, and STDERR
	 */
	if (fd > 2) {
		return -1;
	}
	while (*ptr && (i < len)) {
		usart_send_blocking(USART1, *ptr);
		// if (*ptr == '\n') {
		// 	usart_send_blocking(USART1, '\r');
		// }
		i++;
		ptr++;
	}
	return i;
}

void sys_clock_setup(void)
{
    //system clock
    rcc_clock_setup(&rcc_clock_config[RCC_CLOCK_CONFIG_HSI_PLL_64MHZ]);

}

void uart_setup(void)
{

    //uart pin
    rcc_periph_clock_enable(RCC_USART1);
    rcc_periph_clock_enable(RCC_GPIOB);

    gpio_mode_setup(GPIOB,GPIO_MODE_AF,GPIO_PUPD_NONE,GPIO6|GPIO7);
    gpio_set_af(GPIOB,GPIO_AF0,GPIO6|GPIO7);

    usart_set_baudrate(USART1,115200);
    usart_set_databits(USART1,8);
    usart_set_stopbits(USART1,USART_STOPBITS_1);
    usart_set_parity(USART1,USART_PARITY_NONE);
    usart_set_flow_control(USART1,USART_FLOWCONTROL_NONE);
    usart_set_mode(USART1,USART_MODE_TX_RX);

    //uart isr
    nvic_enable_irq(NVIC_USART1_IRQ);

    usart_enable(USART1);

    usart_enable_rx_interrupt(USART1);
}

/**
 * @brief uart1 isr function
 * 
 */
void usart1_isr(void)
{
    //receive interrupt
	if (((USART_CR1(USART1) & USART_CR1_RXNEIE) != 0) &&
	    ((USART_ISR(USART1) & USART_ISR_RXNE) != 0)) {

        char c = usart_recv(USART1);

        xQueueSendFromISR(uart_queue, &c, NULL);
	}
}

/**
 * @brief systick setup for rtos tick
 */
static void systick_setup(void)
{
	systick_set_clocksource(STK_CSR_CLKSOURCE_AHB);
	systick_set_reload(64*1000);

	systick_interrupt_enable();

	/* Start counting. */
	systick_counter_enable();
}

static void led_task(void *args)
{
    rcc_periph_clock_enable(RCC_GPIOC);
    gpio_mode_setup(GPIOC,GPIO_MODE_OUTPUT,GPIO_PUPD_NONE,GPIO12);

    while (1)
    {
        gpio_toggle(GPIOC,GPIO12);
        vTaskDelay(pdMS_TO_TICKS(500));
    }
}


static BaseType_t prvHelloCommand( 
    int8_t *pcWriteBuffer, 
    size_t xWriteBufferLen,
    const int8_t *pcCommandString ) 
{

    sprintf(pcWriteBuffer, "hello world command");
 
    /* Execution of this command is complete, so return pdFALSE. */
    return pdFALSE;
}

static const CLI_Command_Definition_t xHelloCommand = 
{
    "hello",
    "\r\nhello:\r\n This is a hello command for testing\r\n",
    prvHelloCommand,
    0
};

static void hello_cli_task(void *args)
{
    //register cli command
    FreeRTOS_CLIRegisterCommand(&xHelloCommand);

    char *outbuff;
    static int8_t inbuff[64];
    static int8_t lastinbuff[64];
    uint8_t index =  0;
    BaseType_t ret;

    outbuff = FreeRTOS_CLIGetOutputBuffer();

    printf(">");//command prompt
    fflush(stdout);
    // printf("\r\n");

    char c;

    while(1){

        if( xQueueReceive(uart_queue, &c, 5) == pdPASS) {
            printf("%c",c);//echo
            fflush(stdout);

            /* Was it the end of the line? */
			if( c == '\n' || c == '\r' )
			{
                printf("\r\n");
                fflush(stdout);

                /* See if the command is empty, indicating that the last command is to be executed again. */
                if( index == 0 )
                {
                    /* Copy the last command back into the input string. */
                    strcpy( inbuff, lastinbuff );
                }

                /* Pass the received command to the command interpreter.  The
                command interpreter is called repeatedly until it returns
                pdFALSE	(indicating there is no more output) as it might
                generate more than one string. */
                do
                {
                    /* Get the next output string from the command interpreter. */
                    ret = FreeRTOS_CLIProcessCommand( inbuff, outbuff, configCOMMAND_INT_MAX_OUTPUT_SIZE );

                    /* Write the generated string to the UART. */
                    printf("%s",outbuff);
                    fflush(stdout);

                } while( ret != pdFALSE );

                /* All the strings generated by the input command have been
                sent.  Clear the input string ready to receive the next command.
                Remember the command that was just processed first in case it is
                to be processed again. */
                strcpy( lastinbuff, inbuff );
                index = 0;
                memset( inbuff, 0x00, 64 );

                printf("\r\n>");
                fflush(stdout);

			}
			else
			{
				if( c == '\r' )
				{
					/* Ignore the character. */
				}
				else if( ( c == '\b' ) || ( c == 0x7f ) )//del
				{
					/* Backspace was pressed.  Erase the last character in the
					string - if any. */
					if( index > 0 )
					{
						index--;
						inbuff[ index ] = '\0';
					}
				}
				else
				{
					/* A character was entered.  Add it to the string entered so
					far.  When a \n is entered the complete	string will be
					passed to the command interpreter. */
					if( ( c >= ' ' ) && ( c <= '~' ) )
					{
						if( index < 64 )
						{
							inbuff[ index ] = c;
							index++;
						}
					}
				}
			}

        }
    }

}

int main(void)
{
    sys_clock_setup();

    systick_setup();

    uart_setup();

    //create queue
    uart_queue = xQueueCreate(16, sizeof(char));

    xTaskCreate(led_task,"led task", 256, NULL,2,NULL);

    xTaskCreate(hello_cli_task, "hello cli task", 512, NULL,2,NULL);

    vTaskStartScheduler();
	
	while(1){}

    return 0;
}
